// 观察者模式中 通知

class Dep {
    constructor() {
        // 存放所有watcher
        // this.subs = {0:{update},1:{update}}
        this.subs = {}
    }

    addSub(target) {
        this.subs[target.uid] = target
    }

    notify() {
        for (let uid in this.subs) {
            this.subs[uid].update()
        }
    }
}

/**
 * 观察者
 * scope: 作用域
 * exp: 表达式
 * callback: 回调函数
 */
var $uid = 0;
class Watcher {
    constructor(scope, exp, callback) {
        this.exp = exp;
        this.scope = scope;
        this.callback = callback;
        this.uid = $uid++;
        // 执行更新操作
        this.update();
    }

    // 更新方法
    update() {
        // 获取表达式最新的状态数据
        const value = this.get();
        // 执行回调函数更新节点内容
        this.callback(value);
    }

    // 获取表达式的最新值
    get() {
        // 将当前Watcher实例对象注册为Dep的一个静态属性
        Dep.target = this;
        // 定义方法
        const fn = new Function('scope', 'with(scope){ return ' + this.exp + '}');
        // 调用方法
        let newValue = fn(this.scope);
        // 清空静态属性target, 避免重复添加订阅者
        // Dep.target = null;
        return newValue;
    }
}

/**
 * vue核心类
 */
class Vue {
    constructor(options) {
        this.$options = options;
        this.$data = options.data;
        this.$el = options.el;

        // 将methods方法挂载到this对象上, 方便调用
        this._proxyMethods(options.methods);
        // 将data数据挂载到this对象上, 方便调用
        this._proxyData(this.$data);

        // 通过数据劫持实现响应式数据
        this.defineReactive(this.$data);
        // 实现模板编译
        this.compiler(this.$el);
    }

    /**
     * 代理methods方法
     */
    _proxyMethods(methods) {
        Object.keys(methods).forEach(key => {
            this[key] = methods[key];
        });
    }
    /**
     * 代理data数据
     */
    _proxyData(data) {
        Object.keys(data).forEach(key => {
            // 通过数据劫持的方式将数据挂载this对象上
            Object.defineProperty(this, key, {
                get() {
                    // 当访问this[key]时, 将真实数据data[key]返回
                    return data[key];
                },
                set(value) {
                    // 更新this[key]时, 更新真实数据data[key]
                    data[key] = value;
                }
            });
        });
    }

    /**
     *  定义响应式数据
     */
    defineReactive(data) {
        // 创建发布者订阅者对象
        const dep = new Dep();
        if (!data || typeof data !== 'object') { // 递归结束条件
            return false;
        }
        Object.keys(data).forEach(key => {
            // 中间变量用来记录属性值, 否则会死循环
            let value = data[key];
            Object.defineProperty(data, key, {
                get() {
                    // 添加订阅者(Dep.target为Watcher实例对象)
                    Dep.target && dep.addSub(Dep.target);
                    return value;
                },
                set(newValue) {
                    value = newValue;
                    //  数据更新之后通知订阅者
                    dep.notify();
                }
            });
            // 通过递归实现响应式数据
            this.defineReactive(data[key]);
        });
    }

    // 模板编译方法
    compiler() {
        // 获取模板dom对象
        const node = document.querySelector(this.$el);
        node.childNodes.forEach(child => {
            // 元素节点
            if (child.nodeType === 1) {
                this.compilerElementNode(child);
            } else if (child.nodeType == 3) { // 文本节点
                this.compilerTextNode(child);
            }
        });
    }

    /**
     * 编译元素节点
     * @param {*} node 
     */
    compilerElementNode(node) {
        // 获取节点属性
        let attrs=[...node.attributes];
        attrs.forEach(attr=>{
            // console.log(attr.name,attr.value);
            let directName=attr.name.slice(2);
            switch(directName){
                case 'text':
                    // 添加watcher
                    new Watcher(this.$data, attr.value, (newValue) => {
                        // 更新节点内容                    
                        node.textContent = newValue;
                    });
                break;
                case 'model':
                    // 添加watcher
                    new Watcher(this.$data, attr.value, (newValue) => {
                        // 更新节点内容                    
                        node.value = newValue;
                    });
                    // 注册事件
                    node.addEventListener('input',(event)=>{
                        // 将视图层的更新同步到数据层
                        this.$data[attr.value]=event.target.value;
                    });
                break;
            }
            // 事件注册
            if(attr.name.indexOf('@')===0){
                let eventName=attr.name.slice(1);
                let eventHandle=this[attr.value].bind(this);
                node.addEventListener(eventName,eventHandle);
            }
        });
        
        
        // 执行编译文本方法
        this.compilerTextNode(node);
    }

    /**
     *  编译文本节点
     * @param {*} node 
     */
    compilerTextNode(node) {
        let text = node.textContent.trim();
        if (text) {
            // 解析节点内容, 得出一个表达式
            let exp = this.parseText(text);
            // 通过一个Watcher实例得到表达式最新的计算结果
            // 每一个Watcher实例记录了视图和数据的对应关系
            new Watcher(this.$data, exp, (newValue) => {
                // 更新节点内容                    
                node.textContent = newValue;
            });
        }
    }

    /** 
     * 根据匹配结果, 计算一个出来一个表达式
     * 
     */
    parseText(text) {
        // 定义正则表达式, 匹配插值表示
        const reg = /\{\{(.+?)\}\}/g;
        // 使用正则表达式切割元素内容
        let pieces = text.split(reg);
        // 使用正则表达式匹配元素内容
        let matches = text.match(reg);
        // 存放表达式的每一个组成部分
        let items = [];
        pieces.forEach(item => {
            if (matches && matches.indexOf('{{' + item + '}}') !== -1) {
                items.push('(' + item + ')');
            } else {
                items.push('"' + item + '"');
            }
        });
        return items.join('+');
    }

}